#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/fs.h>  
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/timer.h>
#include <linux/uaccess.h>
#include <linux/cdev.h> 
#include <linux/i2c.h>
/**********************************************************************************
Copyright (C), by AppleCai
Project                      : Study Kernel
Description                  : BSP level for cdev
History                      :
CPU and Compiler             : AM335x,ARM-LINIX-GCC
GenDate                      : 2020-11-14
OwnerBy                      : Apple Cai
**********************************************************************************/
#define APPLEEEP_CNT	1
#define APPLEEEP_NAME	"AppleEEP"

struct AppleEEP_dev {
	dev_t devid;			/* 设备号 	 */
	struct cdev cdev;		/* cdev 	*/
	struct class *class;	/* 类 		*/
	struct device *device;	/* 设备 	 */
	struct device_node	*nd; /* 设备节点 */
	int major;			/* 主设备号 */
	void *private_data;	/* 私有数据 */
};

static struct AppleEEP_dev AppleEEPdev;

/*
 * @description		: 打开设备
 * @param - inode 	: 传递给驱动的inode
 * @param - filp 	: 设备文件，file结构体有个叫做private_data的成员变量
 * 					  一般在open的时候将private_data指向设备结构体。
 * @return 			: 0 成功;其他 失败
 */
static int AppleEEP_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &AppleEEPdev;
	return 0;
}


/*
 * @description		: 从设备读取数据 
 * @param - filp 	: 要打开的设备文件(文件描述符)
 * @param - buf 	: 返回给用户空间的数据缓冲区
 * @param - cnt 	: 要读取的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 读取的字节数，如果为负值，表示读取失败
 */
static ssize_t AppleEEP_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *off)
{
	long err = 0;
	u8 reg[2];
	char *data;
	struct AppleEEP_dev *dev = (struct AppleEEP_dev *)filp->private_data;
	
	int ret;
	struct i2c_msg msg[2];
	struct i2c_client *client = (struct i2c_client *)dev->private_data;
	
	//b[0] = 0;					/* 寄存器首地址 */
	//memcpy(&b[1],buf,len);		/* 将要写入的数据拷贝到数组b里面 */

    data = kmalloc(sizeof(data) * cnt,GFP_KERNEL);
	
	err = copy_from_user(data, buf, cnt);
	reg[0] =0;
	memcpy(&reg[1],data,cnt);
	/* msg[0]为发送要读取的首地址 */
	msg[0].addr = client->addr;			/* AppleEEP地址 */	
	msg[0].flags = 0;	
	msg[0].buf = &reg[0];		            /* AppleEEP地址 */	
	msg[0].len = cnt+1;  				/* 要读取的数据长度*/

	ret = i2c_transfer(client->adapter, msg, 1);
	if(ret == 1) {
		ret = 0;
	} else {
		printk("i2c write failed=%d reg=%06x len=%d\n",ret, reg[0], cnt);
		ret = -EREMOTEIO;
	}
	return ret;
}

/*
 * @description		: 从设备读取数据 
 * @param - filp 	: 要打开的设备文件(文件描述符)
 * @param - buf 	: 返回给用户空间的数据缓冲区
 * @param - cnt 	: 要读取的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 读取的字节数，如果为负值，表示读取失败
 */
static ssize_t AppleEEP_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
	long ret = 0;
	u8 reg=0;
	char *data;
	struct AppleEEP_dev *dev = (struct AppleEEP_dev *)filp->private_data;
	
	struct i2c_msg msg[2];
	struct i2c_client *client = (struct i2c_client *)dev->private_data;

    data = kmalloc(sizeof(data) * cnt,GFP_KERNEL);
    
	/* msg[0]为发送要读取的首地址 */
	msg[0].addr = client->addr;			/* AppleEEP地址 */
	msg[0].flags = 0;
	msg[0].buf = &reg;					/* 读取的首地址 */
	msg[0].len = 1;						/* reg长度*/
	/* msg[1]读取数据 */
	msg[1].addr = client->addr;			/* AppleEEP地址 */
	msg[1].flags = I2C_M_RD;			/* 标记为读取数据*/
	msg[1].buf = data;					/* 读取数据缓冲区 */
	msg[1].len = cnt;  				/* 要读取的数据长度*/

	ret = i2c_transfer(client->adapter, msg, 2);
	if(ret == 2) {
		ret = 0;
	} else {
		printk("i2c rd failed=%ld reg=%06x len=%d\n",ret, reg, cnt);
		ret = -EREMOTEIO;
	}


	ret = copy_to_user(buf, data, cnt);
	if(ret<0)
	{
		printk("copy error\n");
	}
	return ret;

}

/*
 * @description		: 关闭/释放设备
 * @param - filp 	: 要关闭的设备文件(文件描述符)
 * @return 			: 0 成功;其他 失败
 */
static int AppleEEP_release(struct inode *inode, struct file *filp)
{
	return 0;
}

/* AppleEEP操作函数 */
static const struct file_operations AppleEEP_ops = {
	.owner = THIS_MODULE,
	.open = AppleEEP_open,
	.read = AppleEEP_read,
	.write = AppleEEP_write,
	.release = AppleEEP_release,
};

 /*
  * @description     : i2c驱动的probe函数，当驱动与
  *                    设备匹配以后此函数就会执行
  * @param - client  : i2c设备
  * @param - id      : i2c设备ID
  * @return          : 0，成功;其他负值,失败
  */
static int AppleEEP_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	/* 1、申请设备号 */
	alloc_chrdev_region(&AppleEEPdev.devid, 0, APPLEEEP_CNT, APPLEEEP_NAME);
	AppleEEPdev.major = MAJOR(AppleEEPdev.devid);

	/* 2、注册设备 */
	cdev_init(&AppleEEPdev.cdev, &AppleEEP_ops);
	cdev_add(&AppleEEPdev.cdev, AppleEEPdev.devid, APPLEEEP_CNT);

	/* 3、创建类 */
	AppleEEPdev.class = class_create(THIS_MODULE, APPLEEEP_NAME);
	if (IS_ERR(AppleEEPdev.class)) {
		return PTR_ERR(AppleEEPdev.class);
	}

	/* 4、创建设备 */
	AppleEEPdev.device = device_create(AppleEEPdev.class, NULL, AppleEEPdev.devid, NULL, APPLEEEP_NAME);
	if (IS_ERR(AppleEEPdev.device)) {
		return PTR_ERR(AppleEEPdev.device);
	}

	AppleEEPdev.private_data = client;

	return 0;
}

static int AppleEEP_remove(struct i2c_client *client)
{
	/* 删除设备 */
	cdev_del(&AppleEEPdev.cdev);
	unregister_chrdev_region(AppleEEPdev.devid, APPLEEEP_CNT);

	/* 注销掉类和设备 */
	device_destroy(AppleEEPdev.class, AppleEEPdev.devid);
	class_destroy(AppleEEPdev.class);
	return 0;
}

static const struct of_device_id AppleEEP_of_match[] = {
	{ .compatible = "applecai,AppleEEP" },
	{ /* Sentinel */ }
};

/* i2c驱动结构体 */	
static struct i2c_driver AppleEEP_driver = {
	.probe = AppleEEP_probe,
	.remove = AppleEEP_remove,
	.driver = {
			.owner = THIS_MODULE,
		   	.name = "AppleEEP",
		   	.of_match_table = AppleEEP_of_match, 
		   },
};
		   
static int __init AppleEEP_init(void)
{
	int ret = 0;

	ret = i2c_add_driver(&AppleEEP_driver);
	return ret;
}

static void __exit AppleEEP_exit(void)
{
	i2c_del_driver(&AppleEEP_driver);
}

/* module_i2c_driver(AppleEEP_driver) */

module_init(AppleEEP_init);
module_exit(AppleEEP_exit);
MODULE_AUTHOR("AppleCai");  
MODULE_LICENSE("GPL");

 


